#include "ftdiDevices.h"

#include <iostream>
#include <stdlib.h>
#include <functional>

#include "timeClickCnt.h"

ftdiDevices::ftdiDevices()
{
    m_read_thread_ = nullptr;
    m_handle_ = nullptr;
    m_serial_ = "";
    m_description_ = "";
    m_dev_index_ = -1;
    m_thread_start_flag_ = false;
    m_start_cap_flag_ = false;
}

ftdiDevices::~ftdiDevices()
{
    Stop();
}

void ftdiDevices::SetVidPid(DWORD dwVID, DWORD dwPID)
{
    FT_SetVIDPID(dwVID, dwPID);
}

void ftdiDevices::SetDeviceNode(FT_DEVICE_LIST_INFO_NODE ftdiNode, int index)
{
    m_dev_node_ = ftdiNode;
    m_serial_ = ftdiNode.SerialNumber;
    m_description_ = ftdiNode.Description;
    m_dev_index_ = index;
}


int ftdiDevices::InitDevice()
{
    std::vector<FT_DEVICE_LIST_INFO_NODE> ftdiDevices;

    // get the currently connected Ftdi devices
    std::cout << "Detecting devices..." << std::endl;
    if (!FindFtdiDevices(ftdiDevices))
    {
        std::cout << "DetectFtdiDevices error." << std::endl;
        return -1;
    }

    PrintFtdiDevices(ftdiDevices);

    SetDeviceNode(ftdiDevices[0], 0);

    if(OpenDevice(m_dev_index_) < 0){
        return -1;
    }

    return 0;
}

int ftdiDevices::OpenDevice(int deviceNumber)
{
    if(m_handle_ != nullptr){
        std::cout << "Device is already open" << std::endl;
        return 1;
    }

    FT_STATUS status = FT_Open(deviceNumber, &m_handle_);
    if(status != FT_OK){
        std::cout << "Failed to open device " << m_serial_ << std::endl;
        return -1;
    }
    
    std::cout << "Opened device " << m_serial_ << " has handle " << m_handle_ << std::endl;
    // 设置同步FIFO模式
    status = FT_SetBitMode(m_handle_, 0x00, FT_BITMODE_RESET); //重置模式
    if (status != FT_OK)
    {
        std::cout << m_serial_ << " FT_SetBitMode result " << status << std::endl;
        return -2;
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    status = FT_SetBitMode(m_handle_, 0xFF, FT_BITMODE_SYNC_FIFO); // 0x40 = 同步FIFO
    if (status != FT_OK)
    {
        std::cout << m_serial_ << " FT_SetBitMode result " << status << std::endl;
        return -2;
    }

    //1、接收缓冲区
    status = FT_SetUSBParameters(m_handle_, 64*1024, 64 * 1024);
    if (status != FT_OK)
    {
        std::cout << m_serial_ << " FT_SetUSBParameters result " << status << std::endl;
        return -2;
    }
    
    //设置延迟时间  默认是16ms
    status = FT_SetLatencyTimer(m_handle_, 1);
    if (status != FT_OK)
    {
        std::cout << m_serial_ << " FT_SetLatencyTimer result " << status << std::endl;
        return -2;
    }
    // 启用硬件流控
    status = FT_SetFlowControl(m_handle_, FT_FLOW_RTS_CTS, 0, 0);
    if (status != FT_OK)
    {
        std::cout << m_serial_ << " FT_SetFlowControl result " << status << std::endl;
        return -2;
    }
    // 设置接收延时
    status = FT_SetTimeouts(m_handle_, 1, 1);
    if (status != FT_OK)
    {
        std::cout << m_serial_ << " FT_SetTimeouts result " << status << std::endl;
        return -2;
    }
    uint8_t bitMode = 0;
    status = FT_GetBitMode(m_handle_, &bitMode); // 0x40 = 同步FIFO
    if (status != FT_OK)
    {
        std::cout << m_serial_ << " FT_SetBitMode result " << status << std::endl;
        return -2;
    }else{
        printf("FT_GetBitMode result %02x\n", bitMode);
    }
    
    //2、清空缓冲区
    status = FT_Purge(m_handle_, FT_PURGE_RX | FT_PURGE_TX); // Purge both Rx and Tx buffers
    if (status != FT_OK)
    {
        printf("purge device failessd with error %d\n",status);
        return -2;
    }
    
    return 0;
}

int ftdiDevices::CloseDevice()
{
    if(m_handle_ == nullptr){
        std::cout << "Device is already closed" << std::endl;
        return 1;
    }
    FT_STATUS status = FT_Close(m_handle_);
    if(status == FT_OK){
        std::cout << "Closed device" << std::endl;
        m_handle_ = nullptr;
    }else{
        std::cout << "Failed to close device " << m_serial_ << " has handle "<< m_handle_ << std::endl;
        return -1;
    }

    return 0;
}

bool ftdiDevices::FindFtdiDevices(std::vector<FT_DEVICE_LIST_INFO_NODE>& findDevices)
{
    findDevices.clear();

    DWORD numDevices = 0;
    FT_STATUS status = FT_CreateDeviceInfoList(&numDevices);
    if(status != FT_OK){
        printf("Failed device find. FT_STATUS : %d", (int)status);
        return false;
    }
    if (findDevices.size() < numDevices)
    {
        findDevices.resize(numDevices);
    }


    status = FT_GetDeviceInfoList(&findDevices[0], &numDevices);
    if (status != FT_OK)
    {
        printf("FFT_GetDeviceInfoList failed with : %d", (int)status);
        return false;
    }

    return true;
}

void ftdiDevices::PrintFtdiDevices(std::vector<FT_DEVICE_LIST_INFO_NODE>& devices)
{
    DWORD numDevices = devices.size();

    std::cout << "Detected " << numDevices << " devices" << std::endl;

    for (DWORD deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex)
    {
        auto device = devices[deviceIndex];
        std::cout << " Device " << deviceIndex << ", Serial '" << device.SerialNumber << "' Description '" << device.Description << "'" << std::endl;

        //获取指定设备的详细信息
        DWORD Flags;
        DWORD ID;
        DWORD Type;
        DWORD LocId;
        char SerialNumber[16];
        char Description[64];
        //获取设备0的信息
        FT_STATUS ftStatus = FT_GetDeviceInfoDetail(deviceIndex, &Flags, &Type, &ID, &LocId, SerialNumber, Description, &m_handle_);
        if (ftStatus == FT_OK)
        {
            printf("DAQ ---- Dev %d:\n", deviceIndex);
            printf("DAQ ---- Flags=0x%x\n",Flags);
            printf("DAQ ---- Type=0x%x\n",Type);
            printf("DAQ ---- ID=0x%x\n",ID);
            printf("DAQ ---- LocId=0x%x\n",LocId);
            printf("DAQ ---- SerialNumber=%s\n",SerialNumber);
            printf("DAQ ---- Description=%s\n",Description);
        }
        else
        {
            printf("DAQ ---- GetDeviceInfoDetail device 0 Failed with error %d\n", ftStatus);
        }
    }
    std::cout << "-------------" << std::endl;
}

int ftdiDevices::Start()
{
    m_thread_start_flag_ = true;
    m_read_thread_.reset(new std::thread(std::bind(&ftdiDevices::LoopFunction, this)));
    return 0;
}

int ftdiDevices::Stop()
{  
    std::cout << "read thread stop" << std::endl;
    if(m_read_thread_  && m_read_thread_->joinable()){
        m_thread_start_flag_ = false;
        m_read_thread_->join();
        m_read_thread_.reset();
    }
    
    return 0;
}

void ftdiDevices::addCallBack(READ_CALLBACK cb, void* param)
{
    process_data_callBack_ = cb;
    param_ = param;
}

void ftdiDevices::LoopFunction()
{
    std::cout << "read thread start" << std::endl;
    TimeClickCnt per_B;
    size_t total_len = 0;
    per_B.startCnt();
    while (m_thread_start_flag_)
    {
        static int cnt = 0;
        if(cnt >=200){
            double duration = per_B.stopCnt();
            double rate = double(total_len) / duration * 1000.0 / 1024.0 / 1024.0;//  MB/s
            printf("USB transfer rate:%3.3lfMB/s\n", rate); 
            //reset
            total_len = 0;
            cnt = 0;
            per_B.startCnt();
        }
        ++cnt;
        if(!m_start_cap_flag_){
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            continue;
        }
        /* code */
        TimeClickCnt tc;
        tc.startCnt();
        
        //read
        uint8_t rxBuffer[DATA_BUF_SIZE];
        int ret = readData(rxBuffer);
        if(ret < 0){
            if(InitDevice() < 0){
                std::this_thread::sleep_for(std::chrono::milliseconds(3*1000));
                continue;
            }
        } 
        total_len += ret;
        
        //process
        if(process_data_callBack_)
            process_data_callBack_(param_, rxBuffer, ret);
        double dura = tc.stopCnt();

        
        if(dura < 5.0)
            std::this_thread::sleep_for(std::chrono::milliseconds(int(5.0-dura)));
        else{
            printf("======================\n");
            printf("duration: %9.4lfms\n", dura); 
            printf("======================\n"); 
        }

            
    }

    std::cout << "read thread end" << std::endl;
    std::cout << "close" << std::endl;
    CloseDevice();
    
    return;
}

int ftdiDevices::StartCap()
{
    //1.清除缓冲区
    FT_STATUS status = FT_Purge(m_handle_, FT_PURGE_RX | FT_PURGE_TX); // Purge both Rx and Tx buffers
    if (status != FT_OK)
    {
        printf("purge device failed with error %d\n",status);
        return -2;
    }

    //2.开始采集  后面同步采集功能。
    m_start_cap_flag_ = true;
    return 0;
}

int ftdiDevices::readData(uint8_t* RxBuffer)
{
    if(m_handle_ == nullptr){
        std::cout << "dev don't open!" << std::endl;
        return -1;
    }
        
    DWORD dwRxSize = 0;	
    DWORD dwBytesRead = 0;
    FT_STATUS ftStatus = FT_GetQueueStatus(m_handle_, &dwRxSize);
    
    if(ftStatus == FT_OK) {
        if (dwRxSize < USB_BUF_SMALLEST_SIZE) {
            std::cout << "data buf is small " << dwRxSize << std::endl;
            return 0;
        }
        if(dwRxSize > DATA_BUF_SIZE){
            dwRxSize = DATA_BUF_SIZE;
        }

        if((ftStatus = FT_Read(m_handle_, RxBuffer, dwRxSize, &dwBytesRead)) != FT_OK) {
            printf("Error: FT_Read returned %d\n", (int)ftStatus);
            return -1;
        }
        else {
            // printf("FT_Read read %d bytes\n", (int)dwBytesRead);
            // printf("data: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", 
            //     RxBuffer[0], RxBuffer[1], RxBuffer[2], RxBuffer[3], RxBuffer[4], RxBuffer[5], RxBuffer[6], RxBuffer[7],RxBuffer[8], RxBuffer[9], RxBuffer[10],
            //     RxBuffer[11], RxBuffer[12], RxBuffer[13], RxBuffer[14], RxBuffer[15],RxBuffer[16], RxBuffer[17], RxBuffer[18], RxBuffer[19], RxBuffer[20], RxBuffer[21]);
            
        }
    }
    else {
        printf("Error: FT_GetQueueStatus returned %d\n", (int)ftStatus);	
        return -1;
    }
    return dwBytesRead;
}